/**
 * 
 */
package edu.acatlan.unam.client.ui;

//Librerías de la clase
import com.google.gwt.canvas.client.Canvas;
import com.google.gwt.canvas.dom.client.Context2d;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.event.dom.client.GestureStartEvent;
import com.google.gwt.event.dom.client.GestureStartHandler;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.dom.client.KeyPressEvent;
import com.google.gwt.event.dom.client.KeyPressHandler;
import com.google.gwt.event.dom.client.MouseDownEvent;
import com.google.gwt.event.dom.client.MouseDownHandler;
import com.google.gwt.event.dom.client.MouseMoveEvent;
import com.google.gwt.event.dom.client.MouseMoveHandler;
import com.google.gwt.event.dom.client.MouseUpEvent;
import com.google.gwt.event.dom.client.MouseUpHandler;
import com.google.gwt.user.client.ui.DecoratorPanel;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;

//Librerías del proyecto
import edu.acatlan.unam.client.ui.controlador.CBDisegnadorLienzo;
import edu.acatlan.unam.client.ui.controlador.lienzo.CBLienzo;
import edu.acatlan.unam.client.ui.controlador.lienzo.CLienzo;
import edu.acatlan.unam.client.ui.controlador.lienzo.OpLienzo;

/**
 * @author jacobo
 * 
 * Esta clase genera el lienzo con el que se dibujarán los modelos
 * para las bases de datos, el menú con el que se seleccionarán las opciones
 *  y responde a las instrucciones del controlador.
 */
public class Lienzo extends DecoratorPanel implements MouseMoveHandler, MouseDownHandler, 
		MouseUpHandler, GestureStartHandler, KeyPressHandler {
	//Propiedades de la clase
	private Canvas lienzo, backBuffer;
	private Context2d contexto, backBufferContext;
	private CLienzo controlador;
	private static final int ANCHO = 1000, ALTO = 600;
	
	/**
	 * Constructor de la clase
	 */
	public Lienzo() {
		lienzo = Canvas.createIfSupported();
		
		if(lienzo == null) {
			this.add(new Label("Tu navegador no soporta HTML5. Por favor actualiza tu navegador para ver el demo."));
			return;
		}
		
		controlador = new CLienzo();
		
		HorizontalPanel hp = new HorizontalPanel();
		MenuLienzo menu = new MenuLienzo(controlador);
		
		lienzo.setWidth(ANCHO + "px");
		lienzo.setHeight(ALTO + "px");
		lienzo.setCoordinateSpaceWidth(ANCHO);
		lienzo.setCoordinateSpaceHeight(ALTO);
		
		controlador.vincularControl(new C());
		
		contexto = lienzo.getContext2d();
		
		backBuffer = Canvas.createIfSupported();
		backBuffer.setCoordinateSpaceWidth(ANCHO);
		backBuffer.setCoordinateSpaceHeight(ALTO);
		
		backBufferContext = backBuffer.getContext2d();
		
		hp.add(menu);
		hp.add(lienzo);
		
		this.add(hp);
		
		lienzo.addMouseMoveHandler(this);
		lienzo.addMouseDownHandler(this);
		lienzo.addMouseUpHandler(this);
		lienzo.addGestureStartHandler(this);
		lienzo.addKeyPressHandler(this);
	}
	
	/**
	 * @author jacobo
	 * 
	 * Esta clase privada ejecuta las instrucciones que el controlador le
	 * da al lienzo.
	 */
	private class C implements CBLienzo {
		
		/* (non-Javadoc)
		 * @see edu.acatlan.unam.client.ui.controlador.lienzo.CBLienzo#getContextoFrontal()
		 */
		public Context2d getContextoFrontal() {
			return contexto;
		}

		/* (non-Javadoc)
		 * @see edu.acatlan.unam.client.ui.controlador.lienzo.CBLienzo#getContextoLogico()
		 */
		public Context2d getContextoLogico() {
			return backBufferContext;
		}

		/* (non-Javadoc)
		 * @see edu.acatlan.unam.client.ui.controlador.lienzo.CBLienzo#getAncho()
		 */
		public int getAncho() {
			return ANCHO;
		}

		/* (non-Javadoc)
		 * @see edu.acatlan.unam.client.ui.controlador.lienzo.CBLienzo#getAlto()
		 */
		public int getAlto() {
			return ALTO;
		}
	}

	/* (non-Javadoc)
	 * @see com.google.gwt.event.dom.client.GestureStartHandler#onGestureStart(com.google.gwt.event.dom.client.GestureStartEvent)
	 */
	public void onGestureStart(GestureStartEvent event) {
		event.getNativeEvent().preventDefault();
		event.getNativeEvent().stopPropagation();
	}

	/* (non-Javadoc)
	 * @see com.google.gwt.event.dom.client.MouseUpHandler#onMouseUp(com.google.gwt.event.dom.client.MouseUpEvent)
	 */
	public void onMouseUp(MouseUpEvent event) {
		if(event.getNativeButton() == NativeEvent.BUTTON_LEFT)
			controlador.estadoBoton = OpLienzo.SOLTADO;
		
		event.getNativeEvent().preventDefault();
		event.getNativeEvent().stopPropagation();
	}

	/* (non-Javadoc)
	 * @see com.google.gwt.event.dom.client.MouseDownHandler#onMouseDown(com.google.gwt.event.dom.client.MouseDownEvent)
	 */
	public void onMouseDown(MouseDownEvent event) {
		if(event.getNativeButton() == NativeEvent.BUTTON_LEFT) {
			controlador.mouseX = event.getRelativeX(lienzo.getElement());
			controlador.mouseY = event.getRelativeY(lienzo.getElement());
			controlador.estadoBoton = OpLienzo.PRESIONADO;
			controlador.actualizar();
		}
		
		event.getNativeEvent().preventDefault();
		event.getNativeEvent().stopPropagation();
	}

	/* (non-Javadoc)
	 * @see com.google.gwt.event.dom.client.MouseMoveHandler#onMouseMove(com.google.gwt.event.dom.client.MouseMoveEvent)
	 */
	public void onMouseMove(MouseMoveEvent event) {
		/* 
		 * Manejador de evento para el movimiento del mouse. Si el mouse se mueve sobre el Canvas
		 * y se había seleccionado alguna de las opciones descritas, hay que actualizar el lienzo.
		 * */
		controlador.mouseX = event.getRelativeX(lienzo.getElement());	//Asignamos la coordenada x relativa al canvas.
		controlador.mouseY = event.getRelativeY(lienzo.getElement());	//Asignamos la coordenada y relativa al canvas.
		
		if(controlador.estadoBoton == OpLienzo.SOLTADO && (controlador.opcion == OpLienzo.SELECCIONAR || 
				controlador.opcion == OpLienzo.RELACION_1_1	|| controlador.opcion == OpLienzo.RELACION_1_N ||
				controlador.opcion == OpLienzo.RELACION_N_M))
			//Colorear el objeto sobre el que nos encontramos
			controlador.estadoBoton = OpLienzo.MOVIENDO;
		else if(controlador.opcion == OpLienzo.SELECCIONAR && 
				(controlador.estadoBoton == OpLienzo.PRESIONADO || controlador.estadoBoton == OpLienzo.ARRASTRANDO)) {
			//Actualizar la posición del objeto seleccionado
			controlador.estadoBoton = OpLienzo.ARRASTRANDO;
			controlador.actualizar();
		}
		
		event.getNativeEvent().preventDefault();
		event.getNativeEvent().stopPropagation();
	}
	
	/**
	 * @param c es el controlador del Panel Principal. 
	 */
	public void vincularControladores(CBDisegnadorLienzo c) {
		controlador.vincular(c);
	}

	/* (non-Javadoc)
	 * @see com.google.gwt.event.dom.client.KeyPressHandler#onKeyPress(com.google.gwt.event.dom.client.KeyPressEvent)
	 */
	public void onKeyPress(KeyPressEvent event) {
		if(event.getNativeEvent().getKeyCode() == KeyCodes.KEY_BACKSPACE) {
			controlador.opcion = OpLienzo.SUPRIMIR;
			controlador.actualizar();
		}
	}
}
